package cg.cmm.alioss.file;

import cg.cmm.alioss.config.OssConfig;
import cg.cmm.alioss.utils.*;
import com.aliyun.core.utils.IOUtils;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.OSSObjectSummary;
import com.aliyun.oss.model.ObjectListing;
import com.aliyun.oss.model.PutObjectResult;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * @author: baibing
 * create time: 2024-12-01 22:30
 * description:
 */
@Slf4j
@Component
public class FileHelper {

    public static final String ORIGINAL_ROOT_PATH = "data";

    // 文件路径分隔符
    private static final String SEPARATOR = "/";

    // 日期格式化器
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    // 阿里云OSS配置
    @Autowired
    private OssConfig ossConfig;

    /**
     * 上传文件到OSS (保存到默认目录：  oss://bucketname/data/YYYY-MM-DD/uuid/filename)
     *
     * @param file       文件
     * @param bucketname 存储桶名称
     * @return
     */
    public FileResultInfo uploadFile(MultipartFile file, String bucketname) {
        return uploadFile(file, bucketname, getDefaultKey(file), null);
    }

    private String getDefaultKey(MultipartFile file) {
        String originalFilename = FileUtils.processFilename(file.getOriginalFilename());
        String filename = FileUtils.validFileName(originalFilename);
        String key = ORIGINAL_ROOT_PATH +
                SEPARATOR + DATE_TIME_FORMATTER.format(LocalDateTime.now()) +
                SEPARATOR + UUID.randomUUID() +
                SEPARATOR + filename;
        return key;
    }

    /**
     * 上传文件到OSS (保存到默认目录：  oss://bucketname/data/YYYY-MM-DD/uuid/filename)
     *
     * @param file         文件
     * @param bucketname   存储桶名称
     * @param ossFilePath  保存到指定对象存储 非必填
     * @param fileSizeUnit 带单位大小 非必填 如2MB  FileSizeUnit.ofMBeye(2L)
     * @return
     */
    public FileResultInfo uploadFile(MultipartFile file, String bucketname, String ossFilePath, FileSizeUnit fileSizeUnit) {
        String filePath;
        if (StringUtils.isEmpty(ossFilePath)) {
            filePath = getDefaultKey(file);
        } else {
            filePath = ossFilePath;
        }
        return checkFileInfo(file, fileSizeUnit, bucketname, filePath);
    }

    private FileResultInfo checkFileInfo(MultipartFile file,
                                         FileSizeUnit fileSizeUnit, String bucketname, String filePath) {
//        暂时不限制文件大小
//        if (isExceed(file, fileSizeUnit)) {
//            log.error("fileSize is exceed");
//            return ResultUtils.error(FileErrorEnum.FILE_SIZE_EXCEED_ERROR, "fileSize is exceed，[" + file.getSize() + " | " + fileSizeUnit.getTotalFileSize() + "]");
//        }
        PutObjectResult putObjectResult = aliyunOSSUploadFile(file, bucketname, filePath);
        if (putObjectResult == null) {
            return ResultUtils.error(FileErrorEnum.UPLOAD_FAILURE, "upload failure");
        }
        return ResultUtils.success((Object) filePath);
    }

    /**
     * 验证文件大小是否超出默认规定限制
     * (doc、docx、csv、xls、xlsx、ppt、pptx、pdf  最大30MB)
     * (jpg、jpeg、png  最大5MB)
     * (rar、avi、mov、mp3、mp4、zip  最大50MB)
     * (txt  最大2MB)
     *
     * @param multipartFile 文件
     * @return
     */
    public Boolean isExceed(MultipartFile multipartFile) {
        String originalFilename = FileUtils.processFilename(multipartFile.getOriginalFilename());
        return multipartFile.getSize() > FileUtils.getDocumentSizeByDocName(originalFilename);
    }

    /**
     * 验证文件大小是否超出规定限制
     *
     * @param multipartFile 文件
     * @param fileSizeUnit  带单位大小 如2MB  FileSizeUnit.ofMBeye(2L)
     * @return
     */
    public Boolean isExceed(MultipartFile multipartFile, FileSizeUnit fileSizeUnit) {
        if (fileSizeUnit == null) {
            return isExceed(multipartFile);
        }
        return multipartFile.getSize() > fileSizeUnit.getTotalFileSize();
    }

    /**
     * 根据OSS路径下载文件
     *
     * @param filePath
     * @param bucketname
     * @param response
     * @throws IOException
     */
    public void downloadFile(String filePath, String bucketname, HttpServletResponse response) throws IOException {
        if (StringUtils.isNoneEmpty(filePath)) {
            OSS ossClient = createOSSClient();
            try (OSSObject ossObject = ossClient.getObject(bucketname, filePath);
                 OutputStream os = response.getOutputStream()) {
                response.setContentType("application/octet-stream");
                response.setHeader("Content-disposition", "attachment;filename=" + FileUtils.processFilename(filePath));

                byte[] bytes = IOUtils.toByteArray(ossObject.getObjectContent());
                os.write(bytes);
            } finally {
                closeOSSClient(ossClient);
            }
        }
    }

    /**
     * 根据OSS路径获取 inputStream
     *
     * @param filePath
     * @param bucketname
     * @throws IOException
     */
    public InputStream inputStream(String filePath, String bucketname) {
        OSS ossClient = createOSSClient();
        try {
            OSSObject ossObject = ossClient.getObject(bucketname, filePath);
            return ossObject.getObjectContent();
        } finally {
            closeOSSClient(ossClient);
        }
    }

    /**
     * 根据OSS路径生成预览地址（默认2小时有效）
     *
     * @param filePath   文件路径
     * @param bucketname OSS存储桶
     * @param duration   有效期 非必填  默认2小时   例子： 30分钟  Duration.ofMinutes(30)
     * @return
     */
    public FileResultInfo preview(String filePath, String bucketname, Duration duration, Integer flag) {
        if (StringUtils.isEmpty(filePath) || StringUtils.isEmpty(bucketname)) {
            return ResultUtils.error(FileErrorEnum.PARAM_NULL_ERROR, "param null");
        }
        String preSignedUrl = aliyunOSSGetPreSignedUrl(bucketname, filePath, duration, flag);
        return ResultUtils.success((Object) preSignedUrl);
    }

    public FileResultInfo preview(List<String> filePaths, String bucketname, Duration duration) {
        // 参数验证
        if (ObjectUtils.isEmpty(filePaths) || StringUtils.isEmpty(bucketname)) {
            log.error("Invalid parameters: filePaths or bucketname is empty.");
            return ResultUtils.error(FileErrorEnum.PARAM_NULL_ERROR, "Invalid parameters: filePaths or bucketname is empty.");
        }

        try {
            // 调用改进后的 aliyunOSSGetPreSignedUrl 方法获取预签名URL
            Map<String, URL> preSignedUrls = aliyunOSSGetPreSignedUrl(bucketname, filePaths, duration);

            // 如果没有有效的预签名URL，则返回错误信息
            if (preSignedUrls == null || preSignedUrls.isEmpty()) {
                log.error("Failed to generate any presigned URLs.");
                return ResultUtils.error(FileErrorEnum.COMMON_ERROR, "Failed to generate any presigned URLs.");
            }

            // 对生成的预签名URL进行endpoint替换
            Map<String, URL> replacedUrls = preSignedUrls.entrySet().stream()
                    .collect(Collectors.toMap(
                            Map.Entry::getKey,
                            entry -> replaceEndpoint(entry.getValue())
                    ));

            // 返回成功结果
            return ResultUtils.success(replacedUrls);
        } catch (Exception e) {
            // 捕获异常并记录错误日志
            log.error("An error occurred while generating presigned URLs: {}", e.getMessage(), e);
            return ResultUtils.error(FileErrorEnum.COMMON_ERROR, "An error occurred while generating presigned URLs.");
        }
    }
    private URL replaceEndpoint(URL url) {
        if (url != null && !ObjectUtils.isEmpty(ossConfig.getPublicEndpoint())) {
            try {
                String originalHost =new URL(ossConfig.getEndpoint()).getHost();
                String publicEndpoint = new URL(ossConfig.getPublicEndpoint()).getHost();
                String urlString = url.toString().replace(originalHost, publicEndpoint);
                return new URL(urlString);
            } catch (Exception e) {
                log.error("Failed to construct URL from string: {}", url.toString(), e);
            }
        }
        return url;
    }


    /**
     * 获取指定前缀下的所有文件键
     *
     * @param bucketname 存储桶名称
     * @param key        前缀
     * @return 文件键列表
     */
    public List<String> getFiles(String bucketname, String key) {
        OSS ossClient = createOSSClient();
        try {
            ObjectListing objectListing = ossClient.listObjects(bucketname, key);
            List<OSSObjectSummary> summaries = objectListing.getObjectSummaries();
            return summaries.stream()
                    .map(OSSObjectSummary::getKey)
                    .collect(Collectors.toList());
        } finally {
            closeOSSClient(ossClient);
        }
    }

    private OSS createOSSClient() {
        return new OSSClientBuilder().build(ossConfig.getEndpoint(), ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret());
    }

    private void closeOSSClient(OSS ossClient) {
        if (ossClient != null) {
            ossClient.shutdown();
        }
    }

    private PutObjectResult aliyunOSSUploadFile(MultipartFile file, String bucketname, String filePath) {
        try (InputStream inputStream = file.getInputStream()) {
            OSS ossClient = createOSSClient();
            try {
                return ossClient.putObject(bucketname, filePath, inputStream);
            } finally {
                closeOSSClient(ossClient);
            }
        } catch (Exception e) {
            log.error("aliyunOSSUploadFile Exception e:", e);
            return null;
        }
    }

    private PutObjectResult aliyunOSSUploadFile(File file, String bucketname, String filePath) {
        try (InputStream inputStream = Files.newInputStream(file.toPath())) {
            OSS ossClient = createOSSClient();
            try {
                return ossClient.putObject(bucketname, filePath, inputStream);
            } finally {
                closeOSSClient(ossClient);
            }
        } catch (Exception e) {
            log.error("aliyunOSSUploadFile Exception e:", e);
            return null;
        }
    }

    private String aliyunOSSGetPreSignedUrl(String bucketname, String objectName, Duration duration, Integer flag) {
        if (StringUtils.isEmpty(bucketname) || StringUtils.isEmpty(objectName)) {
            log.error("param null");
            return null;
        }
        try {
            Date expiration = new Date(System.currentTimeMillis() + duration.toMillis());
            OSS ossClient = createOSSClient();
            try {
                URL url = ossClient.generatePresignedUrl(bucketname, objectName, expiration);
                if (!ObjectUtils.isEmpty(ossConfig.getPublicEndpoint())) {
                    String originalHost =new URL(ossConfig.getEndpoint()).getHost();
                    String publicEndpoint = new URL(ossConfig.getPublicEndpoint()).getHost();
                    return url.toString().replace(originalHost, publicEndpoint);
                }
                return url.toString();
            } finally {
                closeOSSClient(ossClient);
            }
        } catch (Exception e) {
            log.error("aliyunOSSGetPreSignedUrl Exception e:", e);
            return null;
        }
    }

    private Map<String, URL> aliyunOSSGetPreSignedUrl(String bucketname, List<String> objectNames, Duration duration) {
        if (ObjectUtils.isEmpty(bucketname) || ObjectUtils.isEmpty(objectNames)) {
            log.error("param null");
            return null;
        }
        try {
            Date expiration = new Date(System.currentTimeMillis() + duration.toMillis());
            OSS ossClient = createOSSClient();
            try {
                return objectNames.stream()
                        .collect(Collectors.toMap(
                                key -> key,
                                key -> ossClient.generatePresignedUrl(bucketname, key, expiration)
                        ));
            } finally {
                closeOSSClient(ossClient);
            }
        } catch (Exception e) {
            log.error("aliyunOSSGetPreSignedUrl Exception e:", e);
            return null;
        }
    }

    private String getContentType(String objectName) {
        String extension = objectName.substring(objectName.lastIndexOf('.') + 1).toLowerCase();
        switch (extension) {
            case "jpg":
            case "jpeg":
                return "image/jpeg";
            case "png":
                return "image/png";
            case "pdf":
                return "application/pdf";
            case "doc":
                return "application/msword";
            // 添加更多文件类型...
            default:
                return "binary/octet-stream";
        }
    }

    public static void main(String[] args) {


        String publicEndpoint = null;
        try {
            String originalHost = new URL("http://oss-cn-shanghai-internal.aliyuncs.com").getHost();
            publicEndpoint = new URL("http://oss-cn-shanghai.aliyuncs.com").getHost();
            String url = "http://moodshow-dev2.oss-cn-shanghai-internal.aliyuncs.com/data/2024-12-18/aa6b98e0-bce4-43cc-9ee1-33a8bb2266bb/jqm.jpg?Expires=1735554298&OSSAccessKeyId=LTAI5tHC1TuAC78RGvpCJxHx&Signature=%2Bzr0SjDGwVDA64%2FjgPVRpCcEb6Q%3D";
            System.out.println(url.toString().replace(originalHost, publicEndpoint));
        } catch (MalformedURLException e) {

        }

    }
}